#include "precompiled.h"
#include "Lexer.h"

namespace IcyCrystal {

	Lexer::Lexer(FileReader& fileReader) : fileReader(fileReader){
		currentToken = NULL;
		readAheadToken = NULL;
	}


	Lexer::~Lexer()
	{
		if( currentToken ){
			currentToken->clear();
			delete currentToken;
		}
		if( readAheadToken ){
			readAheadToken->clear();
			delete readAheadToken;
		}
	}

	string* Lexer::getKeyword() {
		// Asssume we are already on the first character.
		string* ret = new string;

		*ret += c;
		c = fileReader.next();
		while( isalpha(c) || isdigit(c) || c == '_')  {
			*ret += c;
			c = fileReader.next();
		}

		fileReader.prev();
		return ret;
	}

	void Lexer::getNumber(Token& token) {
		// Store into a string first.
		string* temp = new string;
		bool isFloat = false;

		*temp += c;
		c = fileReader.next();
		while( isdigit(c) || c == '.' ){
			if( c == '.' ){
				// No more points.
				isFloat = true;
				*temp += c;
				c = fileReader.next();
				while( isdigit(c) ){
					*temp += c;
					c = fileReader.next();
				}
				break;
			}

			*temp += c;
			c = fileReader.next();
		}

		fileReader.prev();
		
		// Using C-style way for conversion.

		char* buff = strdup(temp->c_str());

		if( isFloat ){
			float flt = 0.0;
			sscanf(buff, "%f", &flt);

			token.tokenType = TOKEN_FLOAT;
			token.value.floatValue = flt;
		}
		else {
			int intg =0;
			sscanf(buff, "%d", &intg);

			token.tokenType = TOKEN_INT;
			token.value.intValue = intg;
		}

		free(buff);
		delete temp;
	}

	void Lexer::replaceIfIdentifier(Token& token){
		string& val = *token.value.stringValue;
		if( val == "int" )
			token.tokenType = TOKEN_IDENT_INT;
		else if ( val == "float" )
			token.tokenType = TOKEN_IDENT_FLOAT;
		else if( val == "char")
			token.tokenType = TOKEN_IDENT_CHAR;
		else if( val == "return")
			token.tokenType = TOKEN_RETURN;
		

		// Free space reserved by string.
		if( token.tokenType != TOKEN_KEYWORD )
			delete token.value.stringValue;
	}

	string* Lexer::getString(){
		// Assume we are in first character.
		string* ret = new string;
		c = fileReader.next();
		while( c != '"' ){
			if( c == '\\' ){
				c = fileReader.next();
				switch(c){
				case '"':
					*ret += "\"";
					break;
				case '\\':
					*ret += "\\";
					break;
				case 'n':
					*ret += "\n";
					break;
				}
			}
			else {
				*ret += c;
			}

			c = fileReader.next();
		}

		
		return ret;
	}

	void Lexer::readIntoToken( Token& currentToken ){
		// Get rid of whitespaces
		c = fileReader.next();
		while( c == ' ' || c == '\t' || c == '\n' || c == '\r' )
			c = fileReader.next();

		// What do we have?
		if( isalpha(c) ){
			// Keyword possibly identifier.

			currentToken.tokenType = TOKEN_KEYWORD;
			currentToken.value.stringValue = getKeyword();

			// Check if it's an identifier.
			replaceIfIdentifier(currentToken);
		}
		else if( isdigit(c) ){
			// Number
			getNumber(currentToken);
		}
		else if( c == '"' ){
			// String.
			currentToken.tokenType = TOKEN_STRING;
			currentToken.value.stringValue = getString();
		}
		else if( c == '=' ){
			currentToken.tokenType = TOKEN_EQUALS;
		}
		else if( c == '+' ){
			currentToken.tokenType = TOKEN_PLUS;
		}
		else if( c == '-' ){
			currentToken.tokenType = TOKEN_MINUS;
		}
		else if( c == '*' ){
			currentToken.tokenType = TOKEN_MULT;
		}
		else if( c == '/' ){
			currentToken.tokenType = TOKEN_DIV;
		}
		else if( c == ';'){
			currentToken.tokenType = TOKEN_SEMICOLON;
		}
		else if( c == '[' ){
			currentToken.tokenType = TOKEN_LSQUARE;
		}
		else if( c == ']' ){
			currentToken.tokenType = TOKEN_RSQUARE;
		}
		else if( c == '{' ){
			currentToken.tokenType = TOKEN_LCURLY;
		}
		else if( c == '}' ){
			currentToken.tokenType = TOKEN_RCURLY;
		}
		else if( c==','){
			currentToken.tokenType = TOKEN_COMMA;
		}
		else if( c == '(' ){
			currentToken.tokenType = TOKEN_LPARA;
		}
		else if( c== ')' ){
			currentToken.tokenType = TOKEN_RPARA;
		}
		else if( c == 0) {
			// End of file.
			currentToken.tokenType = TOKEN_EOF;
		}
		else {
			cout << "Invalid: " << c << endl;
		}
	}

	Token* Lexer::getNextToken(){

		if( currentToken == NULL && readAheadToken == NULL ){
			// Read the token at position k=0
			currentToken = new Token();
			currentToken->clear();
			readIntoToken(*currentToken);

			// Read the token at position k=1
			readAheadToken = new Token();
			readAheadToken->clear();
			readIntoToken(*readAheadToken);
		}
		else {
			// Current token is advanced to read ahead token.
			currentToken->clear();
			delete currentToken;
			currentToken = readAheadToken;

			// Read the token at position k=1
			readAheadToken = new Token();
			readAheadToken->clear();
			readIntoToken(*readAheadToken);
		}

		return currentToken;
	}

	Token* Lexer::getReadAheadToken() {
		// Copy the current token.
		return readAheadToken;
	}


}
